import numpy as np
import pandas as pd
from scipy.optimize import minimize_scalar
#第一问！
# Load the CSV file to check its content
file_path = 'E:/金融编程/hsiapr_1tick.csv'
data = pd.read_csv(file_path)

# Display the first few rows of the dataframe to understand its structure
data.head()

# Re-import the data with column names
data = pd.read_csv(file_path, names=['Price', 'Volume'])

# 由于数据文件没有包含列名，我们需要根据数据的结构重新导入数据，并添加列名
data = pd.read_csv(file_path, names=['Price', 'Volume'])

# 定义一个函数来计算每M个合约交易后的价格回报率
'''def calculate_price_returns(data, contracts_per_period):
    """
    根据指定的合约数量，计算每次交易后的价格回报率。

    参数:
    - data: 包含'Price'和'Volume'列的DataFrame
    - contracts_per_period: 指定的每次交易合约数量

    返回:
    - returns: 价格回报率的列表
    """
    # 计算累积交易量
    cumulative_volume = data['Volume'].cumsum()
    price_data = data['Price']

    # 确定累积交易量跨越指定合约数量的索引
    indices = [0]  # 从第一个索引开始
    for threshold in range(contracts_per_period, cumulative_volume.iloc[-1], contracts_per_period):
        # 找到累积交易量首次超过当前阈值的索引
        index = cumulative_volume[cumulative_volume > threshold].index[0]
        indices.append(index)

    # 根据确定的索引计算价格回报率
    prices_at_indices = price_data.loc[indices].reset_index(drop=True)
    returns = prices_at_indices.diff() / prices_at_indices.shift(1)
    returns = returns[1:]  # 去除列表开始的NaN值

    return returns.tolist()

# 使用函数示例，这里M设为10合约
example_contracts_per_period = 10
price_returns_example = calculate_price_returns(data, example_contracts_per_period)

# 展示前几个价格回报率
print(price_returns_example[:5])'''

# Optimizing the calculate_price_returns function to handle smaller contract sizes more efficiently
def calculate_price_returns_optimized(data, contracts_per_period):
    """
    根据指定的合约数量，计算每次交易后的价格回报率，针对小的合约数量进行优化。

    参数:
    - data: 包含'Price'和'Volume'列的DataFrame
    - contracts_per_period: 指定的每次交易合约数量

    返回:
    - returns: 价格回报率的列表
    """
    # 计算累积交易量
    cumulative_volume = data['Volume'].cumsum()
    price_data = data['Price']

    # 累积到当前阈值的交易量
    volume_thresholds = range(contracts_per_period, cumulative_volume.iloc[-1] + contracts_per_period,
                              contracts_per_period)

    # 确定累积交易量跨越指定合约数量的索引
    indices = [0]  # 从第一个索引开始
    cum_vol_index = 0  # 累积交易量的当前索引
    for threshold in volume_thresholds:
        # 在累积交易量中寻找第一个超过当前阈值的点
        while cum_vol_index < len(cumulative_volume) and cumulative_volume[cum_vol_index] < threshold:
            cum_vol_index += 1
        if cum_vol_index < len(cumulative_volume):
            indices.append(cum_vol_index)

    # 根据确定的索引计算价格回报率
    prices_at_indices = price_data[indices].reset_index(drop=True)
    returns = prices_at_indices.diff() / prices_at_indices.shift(1)
    returns = returns[1:]  # 去除列表开始的NaN值

    return returns.dropna().tolist()


# 再次尝试使用优化后的函数，M设为10合约
price_returns_optimized = calculate_price_returns_optimized(data, 10)

# 展示前几个价格回报率
print(price_returns_optimized[:5])
print(len(price_returns_optimized)/2)
#第二问！

#取数列前面一半的数量的索引号码，round是取整，四舍五入
num1=round(len(price_returns_optimized)/2 + 0.5)
num2=len(price_returns_optimized)

# 假定 in_sample_returns 是我们之前计算得到的价格回报率的前半部分，下面用price_returns_optimized[:num]来操作
# 定义对数似然函数
def log_likelihood(lambda_value, returns):
    variance = np.var(returns)  # 初始方差估计

    mu=np.mean(returns)# 均值
    log_likelihood = 0
    for ret in returns:
        variance = lambda_value * variance + (1 - lambda_value) * (ret-mu) ** 2
        log_likelihood += -0.5 * np.log(2 * np.pi * variance) - ((ret-mu) ** 2) / (2 * variance)
    return -log_likelihood  # 返回负值，因为我们要最大化对数似然

# 优化λ
'''这段代码使用scipy.optimize.minimize_scalar函数来找到最优化的λ值，该值能够最大化EWMA模型的对数似然函数。这里的步骤包括：
log_likelihood：这是要最大化的对数似然函数。
bounds=(0, 1)：这定义了λ搜索的范围，因为λ的值应该在0和1之间。
args=(in_sample_returns,)：这将in_sample_returns（即样本内回报率数据）作为参数传递给log_likelihood函数。
method='bounded'：指定了优化算法，这里使用的是有界优化方法。
执行完毕后，result.x包含了最优化过程找到的λ值，这个值是根据样本内数据估计出来，以便在EWMA模型中使用'''
result = minimize_scalar(log_likelihood, bounds=(0, 1), args=(price_returns_optimized[:num1],), method='bounded')
optimized_lambda = result.x

print(f"Optimized λ: {optimized_lambda}")

#第三问！
# 假定 out_sample_returns 是价格回报率的后半部分
# 假定 optimized_lambda 是我们之前确定的最优λ值
# 初始化方差为样本外数据的方差，使用的是时间序列的后一半
rolling_variance = np.var(price_returns_optimized[num1:num2])
# 初始化均值为样本内数据的均值，使用的是前一半时间序列的均值作为mu
mu = np.mean(price_returns_optimized[:num1])
# 存储每个时间点的置信区间
confidence_intervals = []
# 遍历样本外的回报率
for ret in price_returns_optimized[num1:num2]:
    # 更新滚动方差
    rolling_variance = optimized_lambda * rolling_variance + (1 - optimized_lambda) * (ret - mu) ** 2
    # 计算当前的预测标准差
    predicted_std = np.sqrt(rolling_variance)
    # 构建当前的置信区间
    confidence_intervals.append((mu - predicted_std, mu + predicted_std))

# 回溯测试
hits = 0
for ret, ci in zip(price_returns_optimized[num1:num2], confidence_intervals):
    if ci[0] <= ret <= ci[1]:
        hits += 1
# 计算置信区间的覆盖率
coverage_rate = hits / (num2 - num1)
print(f"Coverage rate: {coverage_rate}")